home *** CD-ROM | disk | FTP | other *** search
- _GRAPHICS PROGRAMMING COLUMN_
- by Michael Abrash
-
-
-
- [LISTING ONE]
-
-
- /* 3D animation program to view a cube as it rotates in mode X. The viewpoint
- is fixed at the origin (0,0,0) of world space, looking in the direction of
- increasingly negative Z. A right-handed coordinate system is used throughout.
- All C code tested with Borland C++ 2.0 in C compilation mode */
- #include <conio.h>
- #include <dos.h>
- #include <math.h>
- #include "polygon.h"
-
- #define ROTATION (M_PI / 30.0) /* rotate by 6 degrees at a time */
-
- /* Base offset of page to which to draw */
- unsigned int CurrentPageBase = 0;
- /* Clip rectangle; clips to the screen */
- int ClipMinX=0, ClipMinY=0;
- int ClipMaxX=SCREEN_WIDTH, ClipMaxY=SCREEN_HEIGHT;
- /* Rectangle specifying extent to be erased in each page */
- struct Rect EraseRect[2] = { {0, 0, SCREEN_WIDTH, SCREEN_HEIGHT},
- {0, 0, SCREEN_WIDTH, SCREEN_HEIGHT} };
- static unsigned int PageStartOffsets[2] =
- {PAGE0_START_OFFSET,PAGE1_START_OFFSET};
- int DisplayedPage, NonDisplayedPage;
- /* Transformation from cube's object space to world space. Initially
- set up to perform no rotation and to move the cube into world
- space -100 units away from the origin down the Z axis. Given the
- viewing point, -100 down the Z axis means 100 units away in the
- direction of view. The program dynamically changes both the
- translation and the rotation. */
- static double CubeWorldXform[4][4] = {
- {1.0, 0.0, 0.0, 0.0},
- {0.0, 1.0, 0.0, 0.0},
- {0.0, 0.0, 1.0, -100.0},
- {0.0, 0.0, 0.0, 1.0} };
- /* Transformation from world space into view space. Because in this
- application the view point is fixed at the origin of world space,
- looking down the Z axis in the direction of increasing Z, view space is
- identical to world space, and this is the identity matrix */
- static double WorldViewXform[4][4] = {
- {1.0, 0.0, 0.0, 0.0},
- {0.0, 1.0, 0.0, 0.0},
- {0.0, 0.0, 1.0, 0.0},
- {0.0, 0.0, 0.0, 1.0}
- };
- /* All vertices in the cube */
- static struct Point3 CubeVerts[] = {
- {15,15,15,1},{15,15,-15,1},{15,-15,15,1},{15,-15,-15,1},
- {-15,15,15,1},{-15,15,-15,1},{-15,-15,15,1},{-15,-15,-15,1}};
- /* Vertices after transformation */
- static struct Point3
- XformedCubeVerts[sizeof(CubeVerts)/sizeof(struct Point3)];
- /* Vertices after projection */
- static struct Point3
- ProjectedCubeVerts[sizeof(CubeVerts)/sizeof(struct Point3)];
- /* Vertices in screen coordinates */
- static struct Point
- ScreenCubeVerts[sizeof(CubeVerts)/sizeof(struct Point3)];
- /* Vertex indices for individual faces */
- static int Face1[] = {1,3,2,0};
- static int Face2[] = {5,7,3,1};
- static int Face3[] = {4,5,1,0};
- static int Face4[] = {3,7,6,2};
- static int Face5[] = {5,4,6,7};
- static int Face6[] = {0,2,6,4};
- /* List of cube faces */
- static struct Face CubeFaces[] = {{Face1,4,15},{Face2,4,14},
- {Face3,4,12},{Face4,4,11},{Face5,4,10},{Face6,4,9}};
- /* Master description for cube */
- static struct Object Cube = {sizeof(CubeVerts)/sizeof(struct Point3),
- CubeVerts, XformedCubeVerts, ProjectedCubeVerts, ScreenCubeVerts,
- sizeof(CubeFaces)/sizeof(struct Face), CubeFaces};
-
- void main() {
- int Done = 0, RecalcXform = 1;
- double WorkingXform[4][4];
- union REGS regset;
-
- /* Set up the initial transformation */
- Set320x240Mode(); /* set the screen to mode X */
- ShowPage(PageStartOffsets[DisplayedPage = 0]);
- /* Keep transforming the cube, drawing it to the undisplayed page,
- and flipping the page to show it */
- do {
- /* Regenerate the object->view transformation and
- retransform/project if necessary */
- if (RecalcXform) {
- ConcatXforms(WorldViewXform, CubeWorldXform, WorkingXform);
- /* Transform and project all the vertices in the cube */
- XformAndProjectPoints(WorkingXform, &Cube);
- RecalcXform = 0;
- }
- CurrentPageBase = /* select other page for drawing to */
- PageStartOffsets[NonDisplayedPage = DisplayedPage ^ 1];
- /* Clear the portion of the non-displayed page that was drawn
- to last time, then reset the erase extent */
- FillRectangleX(EraseRect[NonDisplayedPage].Left,
- EraseRect[NonDisplayedPage].Top,
- EraseRect[NonDisplayedPage].Right,
- EraseRect[NonDisplayedPage].Bottom, CurrentPageBase, 0);
- EraseRect[NonDisplayedPage].Left =
- EraseRect[NonDisplayedPage].Top = 0x7FFF;
- EraseRect[NonDisplayedPage].Right =
- EraseRect[NonDisplayedPage].Bottom = 0;
- /* Draw all visible faces of the cube */
- DrawVisibleFaces(&Cube);
- /* Flip to display the page into which we just drew */
- ShowPage(PageStartOffsets[DisplayedPage = NonDisplayedPage]);
- while (kbhit()) {
- switch (getch()) {
- case 0x1B: /* Esc to exit */
- Done = 1; break;
- case 'A': case 'a': /* away (-Z) */
- CubeWorldXform[2][3] -= 3.0; RecalcXform = 1; break;
- case 'T': /* towards (+Z). Don't allow to get too */
- case 't': /* close, so Z clipping isn't needed */
- if (CubeWorldXform[2][3] < -40.0) {
- CubeWorldXform[2][3] += 3.0;
- RecalcXform = 1;
- }
- break;
- case '4': /* rotate clockwise around Y */
- AppendRotationY(CubeWorldXform, -ROTATION);
- RecalcXform=1; break;
- case '6': /* rotate counterclockwise around Y */
- AppendRotationY(CubeWorldXform, ROTATION);
- RecalcXform=1; break;
- case '8': /* rotate clockwise around X */
- AppendRotationX(CubeWorldXform, -ROTATION);
- RecalcXform=1; break;
- case '2': /* rotate counterclockwise around X */
- AppendRotationX(CubeWorldXform, ROTATION);
- RecalcXform=1; break;
- case 0: /* extended code */
- switch (getch()) {
- case 0x3B: /* rotate counterclockwise around Z */
- AppendRotationZ(CubeWorldXform, ROTATION);
- RecalcXform=1; break;
- case 0x3C: /* rotate clockwise around Z */
- AppendRotationZ(CubeWorldXform, -ROTATION);
- RecalcXform=1; break;
- case 0x4B: /* left (-X) */
- CubeWorldXform[0][3] -= 3.0; RecalcXform=1; break;
- case 0x4D: /* right (+X) */
- CubeWorldXform[0][3] += 3.0; RecalcXform=1; break;
- case 0x48: /* up (+Y) */
- CubeWorldXform[1][3] += 3.0; RecalcXform=1; break;
- case 0x50: /* down (-Y) */
- CubeWorldXform[1][3] -= 3.0; RecalcXform=1; break;
- default:
- break;
- }
- break;
- default: /* any other key to pause */
- getch(); break;
- }
- }
- } while (!Done);
- /* Return to text mode and exit */
- regset.x.ax = 0x0003; /* AL = 3 selects 80x25 text mode */
- int86(0x10, ®set, ®set);
- }
-
-
-
-
- [LISTING TWO]
-
-
- /* Transforms all vertices in the specified object into view space, then
- perspective projects them to screen space and maps them to screen coordinates,
- storing the results in the object. */
- #include <math.h>
- #include "polygon.h"
-
- void XformAndProjectPoints(double Xform[4][4],
- struct Object * ObjectToXform)
- {
- int i, NumPoints = ObjectToXform->NumVerts;
- struct Point3 * Points = ObjectToXform->VertexList;
- struct Point3 * XformedPoints = ObjectToXform->XformedVertexList;
- struct Point3 * ProjectedPoints =
- ObjectToXform->ProjectedVertexList;
- struct Point * ScreenPoints = ObjectToXform->ScreenVertexList;
-
- for (i=0; i<NumPoints; i++, Points++, XformedPoints++,
- ProjectedPoints++, ScreenPoints++) {
- /* Transform to view space */
- XformVec(Xform, (double *)Points, (double *)XformedPoints);
- /* Perspective-project to screen space */
- ProjectedPoints->X = XformedPoints->X / XformedPoints->Z *
- PROJECTION_RATIO * (SCREEN_WIDTH / 2.0);
- ProjectedPoints->Y = XformedPoints->Y / XformedPoints->Z *
- PROJECTION_RATIO * (SCREEN_WIDTH / 2.0);
- ProjectedPoints->Z = XformedPoints->Z;
- /* Convert to screen coordinates. The Y coord is negated to
- flip from increasing Y being up to increasing Y being down,
- as expected by the polygon filler. Add in half the screen
- width and height to center on the screen */
- ScreenPoints->X = ((int) floor(ProjectedPoints->X + 0.5)) +
- SCREEN_WIDTH/2;
- ScreenPoints->Y = (-((int) floor(ProjectedPoints->Y + 0.5))) +
- SCREEN_HEIGHT/2;
- }
- }
-
-
-
- [LISTING THREE]
-
-
- /* Draws all visible faces (faces pointing toward the viewer) in the specified
- object. The object must have previously been transformed and projected, so
- that the ScreenVertexList array is filled in. */
- #include "polygon.h"
-
- void DrawVisibleFaces(struct Object * ObjectToXform)
- {
- int i, j, NumFaces = ObjectToXform->NumFaces, NumVertices;
- int * VertNumsPtr;
- struct Face * FacePtr = ObjectToXform->FaceList;
- struct Point * ScreenPoints = ObjectToXform->ScreenVertexList;
- long v1,v2,w1,w2;
- struct Point Vertices[MAX_POLY_LENGTH];
- struct PointListHeader Polygon;
-
- /* Draw each visible face (polygon) of the object in turn */
- for (i=0; i<NumFaces; i++, FacePtr++) {
- NumVertices = FacePtr->NumVerts;
- /* Copy over the face's vertices from the vertex list */
- for (j=0, VertNumsPtr=FacePtr->VertNums; j<NumVertices; j++)
- Vertices[j] = ScreenPoints[*VertNumsPtr++];
- /* Draw only if outside face showing (if the normal to the
- polygon points toward the viewer; that is, has a positive
- Z component) */
- v1 = Vertices[1].X - Vertices[0].X;
- w1 = Vertices[NumVertices-1].X - Vertices[0].X;
- v2 = Vertices[1].Y - Vertices[0].Y;
- w2 = Vertices[NumVertices-1].Y - Vertices[0].Y;
- if ((v1*w2 - v2*w1) > 0) {
- /* It is facing the screen, so draw */
- /* Appropriately adjust the extent of the rectangle used to
- erase this page later */
- for (j=0; j<NumVertices; j++) {
- if (Vertices[j].X > EraseRect[NonDisplayedPage].Right)
- if (Vertices[j].X < SCREEN_WIDTH)
- EraseRect[NonDisplayedPage].Right = Vertices[j].X;
- else EraseRect[NonDisplayedPage].Right = SCREEN_WIDTH;
- if (Vertices[j].Y > EraseRect[NonDisplayedPage].Bottom)
- if (Vertices[j].Y < SCREEN_HEIGHT)
- EraseRect[NonDisplayedPage].Bottom = Vertices[j].Y;
- else EraseRect[NonDisplayedPage].Bottom=SCREEN_HEIGHT;
- if (Vertices[j].X < EraseRect[NonDisplayedPage].Left)
- if (Vertices[j].X > 0)
- EraseRect[NonDisplayedPage].Left = Vertices[j].X;
- else EraseRect[NonDisplayedPage].Left = 0;
- if (Vertices[j].Y < EraseRect[NonDisplayedPage].Top)
- if (Vertices[j].Y > 0)
- EraseRect[NonDisplayedPage].Top = Vertices[j].Y;
- else EraseRect[NonDisplayedPage].Top = 0;
- }
- /* Draw the polygon */
- DRAW_POLYGON(Vertices, NumVertices, FacePtr->Color, 0, 0);
- }
- }
- }
-
-
-
- [LISTING FOUR]
-
-
- /* Routines to perform incremental rotations around the three axes */
- #include <math.h>
- #include "polygon.h"
-
- /* Concatenate a rotation by Angle around the X axis to the transformation in
- XformToChange, placing result back in XformToChange. */
- void AppendRotationX(double XformToChange[4][4], double Angle)
- {
- double Temp10, Temp11, Temp12, Temp20, Temp21, Temp22;
- double CosTemp = cos(Angle), SinTemp = sin(Angle);
-
- /* Calculate the new values of the four affected matrix entries */
- Temp10 = CosTemp*XformToChange[1][0]+ -SinTemp*XformToChange[2][0];
- Temp11 = CosTemp*XformToChange[1][1]+ -SinTemp*XformToChange[2][1];
- Temp12 = CosTemp*XformToChange[1][2]+ -SinTemp*XformToChange[2][2];
- Temp20 = SinTemp*XformToChange[1][0]+ CosTemp*XformToChange[2][0];
- Temp21 = SinTemp*XformToChange[1][1]+ CosTemp*XformToChange[2][1];
- Temp22 = SinTemp*XformToChange[1][2]+ CosTemp*XformToChange[2][2];
- /* Put the results back into XformToChange */
- XformToChange[1][0] = Temp10; XformToChange[1][1] = Temp11;
- XformToChange[1][2] = Temp12; XformToChange[2][0] = Temp20;
- XformToChange[2][1] = Temp21; XformToChange[2][2] = Temp22;
- }
-
- /* Concatenate a rotation by Angle around the Y axis to the transformation in
- XformToChange, placing result back in XformToChange. */
- void AppendRotationY(double XformToChange[4][4], double Angle)
- {
- double Temp00, Temp01, Temp02, Temp20, Temp21, Temp22;
- double CosTemp = cos(Angle), SinTemp = sin(Angle);
-
- /* Calculate the new values of the four affected matrix entries */
- Temp00 = CosTemp*XformToChange[0][0]+ SinTemp*XformToChange[2][0];
- Temp01 = CosTemp*XformToChange[0][1]+ SinTemp*XformToChange[2][1];
- Temp02 = CosTemp*XformToChange[0][2]+ SinTemp*XformToChange[2][2];
- Temp20 = -SinTemp*XformToChange[0][0]+ CosTemp*XformToChange[2][0];
- Temp21 = -SinTemp*XformToChange[0][1]+ CosTemp*XformToChange[2][1];
- Temp22 = -SinTemp*XformToChange[0][2]+ CosTemp*XformToChange[2][2];
- /* Put the results back into XformToChange */
- XformToChange[0][0] = Temp00; XformToChange[0][1] = Temp01;
- XformToChange[0][2] = Temp02; XformToChange[2][0] = Temp20;
- XformToChange[2][1] = Temp21; XformToChange[2][2] = Temp22;
- }
-
- /* Concatenate a rotation by Angle around the Z axis to the transformation in
- XformToChange, placing result back in XformToChange. */
- void AppendRotationZ(double XformToChange[4][4], double Angle)
- {
- double Temp00, Temp01, Temp02, Temp10, Temp11, Temp12;
- double CosTemp = cos(Angle), SinTemp = sin(Angle);
-
- /* Calculate the new values of the four affected matrix entries */
- Temp00 = CosTemp*XformToChange[0][0]+ -SinTemp*XformToChange[1][0];
- Temp01 = CosTemp*XformToChange[0][1]+ -SinTemp*XformToChange[1][1];
- Temp02 = CosTemp*XformToChange[0][2]+ -SinTemp*XformToChange[1][2];
- Temp10 = SinTemp*XformToChange[0][0]+ CosTemp*XformToChange[1][0];
- Temp11 = SinTemp*XformToChange[0][1]+ CosTemp*XformToChange[1][1];
- Temp12 = SinTemp*XformToChange[0][2]+ CosTemp*XformToChange[1][2];
- /* Put the results back into XformToChange */
- XformToChange[0][0] = Temp00; XformToChange[0][1] = Temp01;
- XformToChange[0][2] = Temp02; XformToChange[1][0] = Temp10;
- XformToChange[1][1] = Temp11; XformToChange[1][2] = Temp12;
- }
-
-
-
- [LISTING FIVE]
-
-
- /* POLYGON.H: Header file for polygon-filling code, also includes a number of
- useful items for 3D animation. */
-
- #define MAX_POLY_LENGTH 4 /* four vertices is the max per poly */
- #define SCREEN_WIDTH 320
- #define SCREEN_HEIGHT 240
- #define PAGE0_START_OFFSET 0
- #define PAGE1_START_OFFSET (((long)SCREEN_HEIGHT*SCREEN_WIDTH)/4)
- /* Ratio: distance from viewpoint to projection plane / width of projection
- plane. Defines the width of the field of view. Lower absolute values = wider
- fields of view; higher values = narrower */
- #define PROJECTION_RATIO -2.0 /* negative because visible Z
- coordinates are negative */
- /* Draws the polygon described by the point list PointList in color Color with
- all vertices offset by (X,Y) */
- #define DRAW_POLYGON(PointList,NumPoints,Color,X,Y) \
- Polygon.Length = NumPoints; Polygon.PointPtr = PointList; \
- FillConvexPolygon(&Polygon, Color, X, Y);
-
- /* Describes a single 2D point */
- struct Point {
- int X; /* X coordinate */
- int Y; /* Y coordinate */
- };
- /* Describes a single 3D point in homogeneous coordinates */
- struct Point3 {
- double X; /* X coordinate */
- double Y; /* Y coordinate */
- double Z; /* Z coordinate */
- double W;
- };
- /* Describes a series of points (used to store a list of vertices that
- describe a polygon; each vertex is assumed to connect to the two adjacent
- vertices, and the last vertex is assumed to connect to the first) */
- struct PointListHeader {
- int Length; /* # of points */
- struct Point * PointPtr; /* pointer to list of points */
- };
- /* Describes beginning and ending X coordinates of a single horizontal line */
- struct HLine {
- int XStart; /* X coordinate of leftmost pixel in line */
- int XEnd; /* X coordinate of rightmost pixel in line */
- };
- /* Describes a Length-long series of horizontal lines, all assumed to be on
- contiguous scan lines starting at YStart and proceeding downward (describes
- a scan-converted polygon to low-level hardware-dependent drawing code) */
- struct HLineList {
- int Length; /* # of horizontal lines */
- int YStart; /* Y coordinate of topmost line */
- struct HLine * HLinePtr; /* pointer to list of horz lines */
- };
- struct Rect { int Left, Top, Right, Bottom; };
- /* Structure describing one face of an object (one polygon) */
- struct Face {
- int * VertNums; /* pointer to vertex ptrs */
- int NumVerts; /* # of vertices */
- int Color; /* polygon color */
- };
- /* Structure describing an object */
- struct Object {
- int NumVerts;
- struct Point3 * VertexList;
- struct Point3 * XformedVertexList;
- struct Point3 * ProjectedVertexList;
- struct Point * ScreenVertexList;
- int NumFaces;
- struct Face * FaceList;
- };
-
- extern void XformVec(double Xform[4][4], double * SourceVec,
- double * DestVec);
- extern void ConcatXforms(double SourceXform1[4][4],
- double SourceXform2[4][4], double DestXform[4][4]);
- extern void XformAndProjectPoly(double Xform[4][4],
- struct Point3 * Poly, int PolyLength, int Color);
- extern int FillConvexPolygon(struct PointListHeader *, int, int, int);
- extern void Set320x240Mode(void);
- extern void ShowPage(unsigned int StartOffset);
- extern void FillRectangleX(int StartX, int StartY, int EndX,
- int EndY, unsigned int PageBase, int Color);
- extern void XformAndProjectPoints(double Xform[4][4],
- struct Object * ObjectToXform);
- extern void DrawVisibleFaces(struct Object * ObjectToXform);
- extern void AppendRotationX(double XformToChange[4][4], double Angle);
- extern void AppendRotationY(double XformToChange[4][4], double Angle);
- extern void AppendRotationZ(double XformToChange[4][4], double Angle);
- extern int DisplayedPage, NonDisplayedPage;
- extern struct Rect EraseRect[];
-